package com.istock.servicesample.config;

import java.lang.reflect.Field;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;

@Component
public class QiluHystrixCondurrencyStrategy extends HystrixConcurrencyStrategy {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	public QiluHystrixCondurrencyStrategy() {
		HystrixConcurrencyStrategy strategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
        if (strategy instanceof QiluHystrixCondurrencyStrategy) {
            return;
        }
        // 获取原来的相关数据配置
        HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
        HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
        HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
        HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
        logger.debug("Registering qiluHystrix Concurrency Strategy.");

        // 重置再重新填充
        // 直接设置会触发异常 Caused by: java.lang.IllegalStateException: Another strategy was already registered.
        HystrixPlugins.reset();
        HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
        HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
        HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
        HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
        HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
	}
	
	@Override
	public <T> Callable<T> wrapCallable(Callable<T> callable) {
		
		//这段代码在原始现成执行,这边获得原始线程所有的threadLocal
		Object currentThreadlocalMap = getCurrentThreadlocalMap();
		
		return new Callable<T>() {
			@Override
			public T call() throws Exception {
				//当前线程池内现成的ThreadLocal变量
                Object oldThreadlocalMapOfWorkThread = getCurrentThreadlocalMap();
				//传递到当前线程池的线程中去
				setCurrentThreadlocalMap(currentThreadlocalMap);
				try {
					//调用线程池内现成的执行
					return callable.call();
				}finally {
					//线程池内线程不会销毁,还原线程池内线程的ThreadLocal变量
					setCurrentThreadlocalMap(oldThreadlocalMapOfWorkThread);
				}
			}
			
		};
	}

	private Object getCurrentThreadlocalMap() {
        Thread thread = Thread.currentThread();
        try {
            Field field = Thread.class.getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object o = field.get(thread);
            return o;
        } catch (NoSuchFieldException | IllegalAccessException e) {
        	logger.error("{}",e);
        }
        return null;
    }
	
	private void setCurrentThreadlocalMap(Object newThreadLocalMap) {
	    Thread thread = Thread.currentThread();
	    try {
	        Field field = Thread.class.getDeclaredField("threadLocals");
	        field.setAccessible(true);
	        field.set(thread,newThreadLocalMap);

	    } catch (NoSuchFieldException | IllegalAccessException e) {
	    	logger.error("{}",e);
	    }
	}
}
